package jemuillot.BiohazardPanelPuzzle;

import jemuillot.pkg.Utilities.AfterTaste;

import org.anddev.andengine.engine.handler.timer.ITimerCallback;
import org.anddev.andengine.engine.handler.timer.TimerHandler;
import org.anddev.andengine.entity.sprite.Sprite;
import org.anddev.andengine.entity.text.Text;
import org.anddev.andengine.input.touch.TouchEvent;
import org.anddev.andengine.opengl.texture.region.TextureRegion;
import org.anddev.andengine.util.HorizontalAlign;

import android.media.MediaPlayer;
import android.view.KeyEvent;

public class MainGameScene extends BiohazardPanelPuzzleScene {

	static final int LAYER_BGTEXT = LAYER_BACKGROUND + 1;

	static final int LAYER_PANEL = LAYER_BGTEXT + 1;

	static final int LAYER_LIGHT = LAYER_PANEL + 1;

	static final int LAYER_HINT = LAYER_LIGHT + 1;

	static final int LAYERS = LAYER_HINT + 1;

	private Light[][] lights;

	private Text headText;

	private Text winText;

	private Text newGameText;

	private Text hintText;

	private int matrix;

	private int hintSplash;

	private Light[][] hintLights;

	private TimerHandler hintTimer;
	private TimerHandler freeTimer;

	private int lastHintX;

	private int lastHintY;

	private Text freeText;

	private boolean freeMode;

	private Text freeDetailText;

	private int level;

	private float panelCenterX;

	private float panelCenterY;

	private Sprite panel;

	private int halfMatrix;

	private boolean reAnswer;

	private boolean[][] answer;

	private MediaPlayer mediaPlayer;

	public MainGameScene(final BiohazardPanelPuzzle bpp, int level) {
		super(bpp, LAYERS);

		this.level = level;

		scene.setBackgroundEnabled(false);

		scene.getLayer(LAYER_BACKGROUND).addEntity(
				new Sprite(0, 0, bpp.backgroundRegion));

		headText = new Text(0, 0, bpp.font, getString(R.string.light_all),
				HorizontalAlign.CENTER);

		headText.setPosition(getCenterX(headText), HEAD_TEXT_Y);

		scene.getLayer(LAYER_BGTEXT).addEntity(headText);

		freeDetailText = new Text(0, 0, bpp.fontHalf, getString(R.string.free_touch),
				HorizontalAlign.CENTER);

		freeDetailText.setPosition(getCenterX(freeDetailText), HEAD_TEXT_Y);

		scene.getLayer(LAYER_BGTEXT).addEntity(freeDetailText);

		winText = new Text(0, 0, bpp.font, getString(R.string.you_won),
				HorizontalAlign.CENTER);

		winText.setPosition(getCenterX(winText), HEAD_TEXT_Y);

		scene.getLayer(LAYER_BGTEXT).addEntity(winText);

		newGameText = new Text(0, 0, bpp.font, getString(R.string.new_game),
				HorizontalAlign.CENTER) {
			@Override
			public boolean onAreaTouched(final TouchEvent pSceneTouchEvent,
					final float pTouchAreaLocalX, final float pTouchAreaLocalY) {

				if (pSceneTouchEvent.getAction() == TouchEvent.ACTION_DOWN) {
					onNewGame(true);
				}
				return true;
			}
		};

		newGameText.setPosition(getCenterX(newGameText), BOTTOM_TEXT_Y);

		scene.getLayer(LAYER_BGTEXT).addEntity(newGameText);

		freeText = new Text(0, 0, bpp.fontHalf, getString(R.string.free), HorizontalAlign.CENTER) {
			@Override
			public boolean onAreaTouched(final TouchEvent pSceneTouchEvent,
					final float pTouchAreaLocalX, final float pTouchAreaLocalY) {

				if (pSceneTouchEvent.getAction() == TouchEvent.ACTION_DOWN)
					onFreeMode();
				return true;
			}
		};

		freeText.setPosition(getDividedCenterX(freeText, 2, 1), BOTTOM_TEXT_Y);

		scene.getLayer(LAYER_BGTEXT).addEntity(freeText);

		hintText = new Text(0, 0, bpp.fontHalf, getString(R.string.hint), HorizontalAlign.CENTER) {
			@Override
			public boolean onAreaTouched(final TouchEvent pSceneTouchEvent,
					final float pTouchAreaLocalX, final float pTouchAreaLocalY) {

				if (pSceneTouchEvent.getAction() == TouchEvent.ACTION_DOWN)
					onHint();
				return true;
			}
		};

		hintText.setPosition(getDividedCenterX(hintText, 2, 2), BOTTOM_TEXT_Y);

		scene.getLayer(LAYER_BGTEXT).addEntity(hintText);

		scene.setTouchAreaBindingEnabled(true);

		onNewGame(false);
	}

	protected void onFreeMode() {
		freeMode ^= true;

		cancelHint();

		if (freeMode) {

			headText.setVisible(false);

			if (null == freeTimer)
				scene.registerUpdateHandler(freeTimer = new TimerHandler(0.26f,
						true, new ITimerCallback() {

							@Override
							public void onTimePassed(
									final TimerHandler pTimerHandler) {

								if (freeMode) {

									freeText.setVisible(!freeText.isVisible());
									freeDetailText.setVisible(freeText
											.isVisible());

								} else {
									cancelFreeMode();
								}

							}

						}));
		} else
			cancelFreeMode();

	}

	protected void cancelFreeMode() {

		freeDetailText.setVisible(false);

		freeText.setVisible(true);

		headText.setVisible(true);

		scene.unregisterUpdateHandler(freeTimer);

		freeTimer = null;

	}

	protected void onHint() {

		if (reAnswer) {

			LogicalLight[][] logicalLights = new LogicalLight[matrix][matrix];

			for (int y = 0; y < matrix; y++) {
				for (int x = 0; x < matrix; x++) {
					logicalLights[y][x] = new LogicalLight(lights[y][x]);
					answer[y][x] = false;
				}
			}

			boolean[][] current = new boolean[matrix][matrix];

			while (!isAllLighted(logicalLights)) {
				for (int y = 0; y < matrix; y++) {
					for (int x = 0; x < matrix; x++) {
						current[y][x] = !logicalLights[y][x].getLighting();
						answer[y][x] ^= current[y][x];
					}
				}

				for (int y = 0; y < matrix; y++) {
					for (int x = 0; x < matrix; x++) {
						if (current[y][x])
							touchLight(logicalLights, x, y);
					}
				}

			}

		}

		if (freeMode) {
			for (int y = 0; y < matrix; y++) {
				for (int x = 0; x < matrix; x++) {
					hintLights[y][x].setVisible(answer[y][x]);
				}
			}
		} else {
			int hintX = -1;
			int hintY = -1;

			hintSelect:

			while (hintX == -1) {
				for (int y = 0; y < matrix; y++) {
					for (int x = 0; x < matrix; x++) {
						if (answer[y][x]) {
							hintX = x;
							hintY = y;

							if (Math.random() > 0.5) {
								if ((hintX == lastHintX)
										&& (hintY == lastHintY)) {
									continue;
								}

								break hintSelect;
							}

						}
					}
				}

				// MUST BE Already All Lighted by the Free Mode
				if (hintY == -1) {
					return;
				}
			}

			lastHintX = hintX;
			lastHintY = hintY;

			hintLights[lastHintY][lastHintX].setVisible(true);
		}

		hintSplash = 17;

		if (null == hintTimer)
			scene.registerUpdateHandler(hintTimer = new TimerHandler(0.26f,
					true, new ITimerCallback() {

						@Override
						public void onTimePassed(
								final TimerHandler pTimerHandler) {

							if (hintSplash > 0) {

								if (freeMode) {
									for (int y = 0; y < matrix; y++) {
										for (int x = 0; x < matrix; x++) {
											hintLights[y][x]
													.turnOn(1 == hintSplash % 2);
										}
									}
								} else {
									hintLights[lastHintY][lastHintX]
											.turnOn(1 == hintSplash % 2);
								}

								hintSplash--;

							} else {
								cancelHint();
							}

						}

					}));

	}

	protected void onNewGame(boolean levelUp) {

		if (levelUp)
			level++;

		if (level == matrixLevels.length) {
			toLevelSelect();
		} else {
			bpp.updateMaxLevel(level);

			bpp.getEngine().runOnUpdateThread(new Runnable() {
				@Override
				public void run() {
					newStage();
				}
			});
		}

	}

	private void newStage() {

		freeDetailText.setVisible(false);
		winText.setVisible(false);
		newGameText.setVisible(false);

		matrix = matrixLevels[level];

		halfMatrix = halfMatrixLevels[level];

		reAnswer = true;

		answer = new boolean[matrix][matrix];

		scene.getLayer(LAYER_PANEL).clear();

		TextureRegion panelRgn;

		if (matrix == 3) {
			panelRgn = bpp.panelRegion;
		} else {
			panelRgn = bpp.panelRegionX;
		}

		panelCenterX = getCenterX(panelRgn);
		panelCenterY = getCenterY(panelRgn);

		panel = new Sprite(getCenterX(panelRgn), getCenterY(panelRgn), panelRgn) {
			@Override
			public boolean onAreaTouched(final TouchEvent pSceneTouchEvent,
					final float pTouchAreaLocalX, final float pTouchAreaLocalY) {

				float lightDistX;
				float lightDistY;

				float startX;
				float startY;

				if (matrix == 3) {

					lightDistX = LIGHT_DIST_X;
					lightDistY = LIGHT_DIST_Y;

					startX = LIGHT_OFFSET_START_X;
					startY = LIGHT_OFFSET_START_Y;

				} else {

					lightDistX = LIGHTZONE_SIDE / matrix;
					lightDistY = lightDistX;

					startX = panelCenterX;// LIGHTX_STARTX - panelCenterX;
					startY = panelCenterY;// LIGHTX_STARTY - panelCenterY;
				}

				int x = (int) ((pTouchAreaLocalX - startX) / lightDistX);
				int y = (int) ((pTouchAreaLocalY - startY) / lightDistY);

				if ((x >= matrix) || (y >= matrix) || (x < 0) || (y < 0))
					return false;

				if (pSceneTouchEvent.getAction() == TouchEvent.ACTION_DOWN)
					onLightTouched(x, y);

				return true;
			}
		};

		scene.getLayer(LAYER_PANEL).addEntity(panel);

		scene.getLayer(LAYER_LIGHT).clear();

		lights = new Light[matrix][matrix];

		for (int y = 0; y < matrix; y++) {
			for (int x = 0; x < matrix; x++) {
				lights[y][x] = new Light(this, matrix, x, y, panelCenterX,
						panelCenterY);

				lights[y][x].addToLayer(scene.getLayer(LAYER_LIGHT));
			}
		}

		scene.getLayer(LAYER_HINT).clear();

		hintLights = new Light[matrix][matrix];

		for (int y = 0; y < matrix; y++) {
			for (int x = 0; x < matrix; x++) {
				hintLights[y][x] = new Light(this, matrix, x, y, panelCenterX,
						panelCenterY);

				hintLights[y][x].addToLayer(scene.getLayer(LAYER_HINT));

			}
		}

		headText.setVisible(true);
		hintText.setVisible(true);
		freeText.setVisible(true);

		scene.unregisterTouchArea(newGameText);

		scene.registerTouchArea(hintText);
		scene.registerTouchArea(freeText);
		scene.registerTouchArea(panel);

		do {
			for (int y = 0; y < matrix; y++) {
				for (int x = 0; x < matrix; x++) {
					hintLights[y][x].setVisible(false);
					lights[y][x].turnOn(Math.random() > 0.5);
				}
			}
		} while (isAllLighted(lights));

		freeMode = false;

		bpp.sfxDone.stop();

		mediaPlayer = MediaPlayer.create(bpp, R.raw.bgm);
		mediaPlayer.setLooping(true);
		mediaPlayer.start();

	}

	public void onLightTouched(int xid, int yid) {

		bpp.sfxToggle.play();

		cancelHint();

		if (freeMode) {
			reAnswer = true;
			lights[yid][xid].toggle();
		} else {
			touchLight(lights, xid, yid);
			answer[yid][xid] ^= true;
			judge();
		}
	}

	private void cancelHint() {

		for (int y = 0; y < matrix; y++) {
			for (int x = 0; x < matrix; x++) {
				hintLights[y][x].setVisible(false);
			}
		}

		scene.unregisterUpdateHandler(hintTimer);

		hintTimer = null;
	}

	private void touchLight(LogicalLight[][] lights, int xid, int yid) {

		lights[yid][xid].toggle();

		for (int i = -halfMatrix; i <= halfMatrix; i++) {
			if ((xid + i >= 0) && (xid + i < matrix))
				lights[yid][xid + i].toggle();

			if ((yid + i >= 0) && (yid + i < matrix))
				lights[yid + i][xid].toggle();
		}

	}

	private void judge() {

		if (isAllLighted(lights)) {
			
			new AfterTaste(bpp).showDonateClickHint();

			headText.setVisible(false);
			hintText.setVisible(false);
			freeText.setVisible(false);

			winText.setVisible(true);

			scene.unregisterTouchArea(panel);
			scene.unregisterTouchArea(hintText);
			scene.unregisterTouchArea(freeText);

			newGameText.setVisible(true);
			scene.registerTouchArea(newGameText);

			mediaPlayer.stop();

			bpp.sfxDone.play();
		}

	}

	private void toLevelSelect() {
		mediaPlayer.stop();
		new LevelSelectScene(bpp);
	}

	public void onResumed() {
		mediaPlayer.start();
	}

	public void onPaused() {
		mediaPlayer.pause();
	}

	private boolean isAllLighted(LogicalLight[][] lights) {

		for (int y = 0; y < matrix; y++) {
			for (int x = 0; x < matrix; x++) {

				if (!lights[y][x].getLighting())
					return false;
			}
		}

		return true;
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_BACK) {
			toLevelSelect();
			return true;
		} else
			return super.onKeyDown(keyCode, event);
	}
}
